#ifndef ClassifyWithRealTrackingTest_HPP
#define ClassifyWithRealTrackingTest_HPP

#include <cv.h>
#include <highgui.h>
#include <ml.h>

#include "HandInterface.hpp"

#include "AbstractHandTracker.hpp"

#include <iostream>
//TODO clean up includes

#include "gtest.h"



#include "HiVisualiser.hpp"

using namespace cv;
using namespace hi;

//FIX: This is only really a pseudo-header!

namespace {
	
	// The fixture for testing class PerPixelProbabilityTransformerTest.
	class ClassifyWithRealTrackingTest : public ::testing::Test {
	public:
		
		AbstractHandTracker *ht;
		ResourceTracker *rt;
		
		cv::Ptr< cv::VideoCapture > capPtr;
		cv::Ptr< VideoPointTagSequence > pointTagSequencePtr;
		cv::Ptr< VideoGestureTagSequence > gestureTagSequencePtr;
		cv::Mat frame;
		
		HandLocaliser* hl;
		
		cv::Rect trackingRect;
		
		double errorThreshold;
		
		int localisedHandInFrame;
		
	protected:
		
		virtual void SetUp();
		
		virtual void TearDown();
		
		virtual void setHandTracker(int trackerCode)=0;
		
		int initTrackerTest(const char* videoNameString);
		
		void testTracker(int trackerCode, const char* videoNameString);
		
		void trainSVM(Mat& trainingData, Mat& trainingResponses );
	};
	
};


void ::ClassifyWithRealTrackingTest::SetUp() {
	rt = new ResourceTracker( "../config/runtimeSettings/cfg.xml" );
	rt->loadCategory("TestData");
}

void ::ClassifyWithRealTrackingTest::TearDown() {
	delete rt;
	delete ht;
}

int ::ClassifyWithRealTrackingTest::initTrackerTest(const char* videoNameString) {
	//HiVisualiser::addWindow( "Track and Classify Test" );
	
	capPtr = rt->getFileVideoCapture( videoNameString );
	pointTagSequencePtr = rt->getPointTags( videoNameString );
	gestureTagSequencePtr = rt->getGestureTags( videoNameString );
	
	//FIXNOW
	int tagCount = pointTagSequencePtr->tagCount;
	
	hl = new HandLocaliser();
	
	int localisationframeNumber = 0; //NOTE: frame zero is never tracked as it is always used by 
	
	for ( ; localisationframeNumber < tagCount; localisationframeNumber++ ) {
		try {
			(*capPtr) >> frame;
			pointTagSequencePtr->getNextPointTag();
			gestureTagSequencePtr->getNextGestureTag();
			trackingRect = hl->localiseHand( frame );
			
			cout << "Face found. First tracking frame:" << endl;
			
			cerr << localisationframeNumber + 1 << ";" ;
			
			break;
		}
		catch (cv::Exception e) {
			if ( e.code == HI_ERR_NOFACEFOUND ) {
				continue;
			} else {
				throw;
			}
			
		}
	}
	
	errorThreshold  = 0.75 * (trackingRect.width + trackingRect.height);
	
	forceInside( frame.size(), trackingRect);
	
	return localisationframeNumber;
}

void ::ClassifyWithRealTrackingTest::testTracker(int trackerCode, const char* videoNameString ) {
	
//	//VISUALISATION //TODO RAT
//	HiVisualiser::windowMatrixMap["ClassifyWithRealTrackingTest"] = imgToDraw;
//	HiVisualiser::moveWindow("ClassifyWithRealTrackingTest", 700, 150);

	RecordProperty( "videoNameString ", videoNameString);
	int localisationFrameNumber = initTrackerTest( videoNameString ); //returns number of first frame where face was found
	int firstTrackingFrame = localisationFrameNumber+1;
	
	setHandTracker( trackerCode );

	int tagCount = pointTagSequencePtr->tagCount;
	
	//TODO RAT
	//int tagsLeft = tagCount - localisationFrameNumber; //FIX don't assume all tags of both kinds present!
	
	int truePositive = 0;
	int falsePositive = 0;
	int trueNegative = 0;
	int falseNegative = 0;
	
	// -- Prepare timing data -- //
	
	double t = 0;
	t = (double)cvGetTickCount();
	
	for( int frameCount = firstTrackingFrame; frameCount<tagCount; frameCount++) {
		// -- get next frame -- //
		(*capPtr) >> frame;
		
		// -- tracking frame -- //
		trackingRect = ht->track( frame );
		bool classifiedActive = ht->isHandActive();
		
		//VISUALISATION
		if (classifiedActive) {
			circle(HiVisualiser::windowMatrixMap["handtracker"], Point(15, 5), 4, Scalar(0, 0, 255), 2);
		} else {
			circle(HiVisualiser::windowMatrixMap["handtracker"], Point(15, 5), 2, Scalar(0, 255, 0), 2);
		}
		
		// -- get and display gesture tag -- //
		char gestureTag = gestureTagSequencePtr->getNextGestureTag();
		bool taggedActive;
		switch (gestureTag) {
			case 'o':
				circle(HiVisualiser::windowMatrixMap["handtracker"], Point(5, 5), 4, Scalar(0, 0, 255), 2);//VISUALISATION
				taggedActive = true;
				break;
			case 'c':
				circle(HiVisualiser::windowMatrixMap["handtracker"], Point(5, 5), 2, Scalar(0, 255, 0), 2);//VISUALISATION
				taggedActive = false;
				break;
			default:
				CV_Error( CV_StsError, "Unrecognised tag in gesture tags!"); //throws exception!
				break;
		}
		
		//Could consider only testing classification when error in tracking is small?
		if ( classifiedActive ) {
			if ( taggedActive ) {
				truePositive++;
			} else {
				falsePositive++;
			}
		} else {
			if ( taggedActive ) {
				falseNegative++;
			} else {
				trueNegative++;
			}

		}


		
		//VISUALISATION
		HiVisualiser::refreshWindow( "handtracker" );
		waitKey(1);
//		char c = waitKey(0);
//		if( c == 27 ) break;
	}
	
	cout << "truePositive=" << truePositive << endl;
	cout << "falsePositive=" << falsePositive << endl;
	cout << "falseNegative=" << falseNegative  << endl;
	cout << "trueNegative=" << trueNegative << endl;
	
	int sampleCount = truePositive+falsePositive+trueNegative+falseNegative;
	double precision = truePositive / (double) ( truePositive+falsePositive );
	double recall = truePositive / (double) ( truePositive+falseNegative );
	double f1Score = 2*precision*recall / ( precision + recall );
	double groundTruthPositiveFraction = ( truePositive + falseNegative ) / (double) sampleCount; 
	
	cout << "sample count=" << sampleCount << endl;
	cout << "groundTruthPositiveFraction=" << groundTruthPositiveFraction << endl;
	cout << "precision=" << precision << endl;
	cout << "recall=" << recall  << endl;
	cout << "f1 score=" << f1Score << endl;
	
	t = (double)cvGetTickCount() - t;
	
	const double f1AcceptanceThreshold = 0.6;

	//Note: properties should have no spaces in names to give well-formed xml
	RecordProperty( "SampleCount", sampleCount );
	RecordProperty( "GroundTruthPositiveFraction", groundTruthPositiveFraction );
	RecordProperty( "f1AcceptanceThreshold", f1AcceptanceThreshold );
	RecordProperty( "f1Score ", f1Score );
	RecordProperty( "precision", precision );
	RecordProperty( "recall", recall );
	
	double timeTrackingOnly = t/((double)cvGetTickFrequency()*1e6);
	RecordProperty( "TimeTrackingOnly", timeTrackingOnly );
	RecordProperty( "TrackingFPS", tagCount/timeTrackingOnly );
	
	//To make it easy to record data to a csv -- //NOTE: Also data recorded during init
	std::cerr << sampleCount << ";";
	std::cerr << groundTruthPositiveFraction << ";";
	std::cerr << f1AcceptanceThreshold << ";";
	std::cerr << f1Score << ";";
	std::cerr << precision << ";";
	std::cerr << recall << ";";
	
	std::cerr << timeTrackingOnly << ";";
	std::cerr << tagCount/timeTrackingOnly << std::endl;
	
	
	ASSERT_GE( f1Score, f1AcceptanceThreshold );
	
}



// -- Main Method -- //

int main(int argc, char **argv) {
	::testing::InitGoogleTest(&argc, argv);
	return RUN_ALL_TESTS();
}


// -- Example test -- //

//
//TEST_F(ClassifyWithRealTrackingTest, LKGREYaaBPMSaaOF0 ) {
//	testTracker( 0, "locVid1c" );
//}

#endif